package com.pokermind.operator.validation;

import com.pokermind.model.dto.CreateHandRequest;
import com.pokermind.model.exception.HandManagementException;

import java.math.BigDecimal;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.regex.Pattern;

/**
 * 手牌请求验证器 德州扑克业务规则验证，替代注解验证
 *
 * @author PokerMind Team
 */
public class HandRequestValidator {

    private static final Pattern CARD_PATTERN = Pattern.compile("^[2-9TJQKA][shdc]$");
    
    // V4.7: 移除位置常量限制，支持前端灵活定义位置
    // 不同平台和地区可能有不同的位置命名习惯，后端不再强制校验

    /**
     * 验证创建手牌请求 - 基础验证（不包含数据库依赖检查）
     */
    public static void validateCreateHandRequest(CreateHandRequest request) {
        if (request == null) {
            throw new HandManagementException.HandInvalidStateException("null", "valid request");
        }
        // 基本字段验证
        validateBasicFields(request);
        // 盲注验证
        validateBlinds(request);
        // 玩家验证
        validatePlayers(request);
        // 庄家按钮验证
        validateButtonSeat(request);
        // 位置信息验证
        validatePositionInfo(request);
    }

    /**
     * 验证创建手牌请求 - 完整验证（包含数据库依赖检查）
     * V4.7: 支持 (table_id, hand_id) 组合唯一性
     *
     * @param request           创建手牌请求
     * @param handExistsChecker 检查 (hand_id, table_id) 是否已存在的函数，返回true表示已存在
     */
    public static void validateCreateHandRequest(CreateHandRequest request, java.util.function.BiPredicate<String, String> handExistsChecker) {
        // 执行基础验证
        validateCreateHandRequest(request);

        // 执行重复性检查
        validateHandIdUniqueness(request.getHandId(), request.getTableId(), handExistsChecker);
    }

    /**
     * 验证基本字段
     */
    private static void validateBasicFields(CreateHandRequest request) {
        if (isBlank(request.getHandId())) {
            throw new HandManagementException.EmptyFieldException();
        }
        if (request.getHandId().length() > 100) {
            throw new HandManagementException.FieldTooLongException();
        }
        if (isBlank(request.getTableId())) {
            throw new HandManagementException.EmptyFieldException();
        }
        if (request.getTableId().length() > 50) {
            throw new HandManagementException.FieldTooLongException();
        }
    }

    /**
     * V4.8: 验证前注和 Straddle 配置
     * small_blind 和 big_blind 字段已完全移除
     * 盲注行动由前端通过 RECORD_ACTION_REQ 主动发起
     */
    private static void validateBlinds(CreateHandRequest request) {
        // V4.8: small_blind 和 big_blind 字段已移除，不再验证
        
        // 验证前注（如果提供）
        if (request.getAnte() != null && request.getAnte().compareTo(BigDecimal.ZERO) < 0) {
            throw new HandManagementException.InvalidStackException("system", "ante cannot be negative");
        }
        
        // Straddle 配置仅用于记录，不做强制验证
    }

    /**
     * 验证玩家
     */
    private static void validatePlayers(CreateHandRequest request) {
        List<CreateHandRequest.PlayerInfo> players = request.getPlayers();

        if (players == null || players.isEmpty()) {
            throw new HandManagementException.InsufficientPlayersException(0);
        }

        if (players.size() < 2) {
            throw new HandManagementException.InsufficientPlayersException(players.size());
        }

        if (players.size() > 6) {
            throw new HandManagementException.TooManyPlayersException();
        }

        // 验证座位号和玩家ID唯一性
        Set<Integer> seatNumbers = new HashSet<>();
        Set<String> playerIds = new HashSet<>();
        int heroCount = 0;

        for (CreateHandRequest.PlayerInfo player : players) {
            validatePlayer(player);

            // 检查座位号重复
            if (!seatNumbers.add(player.getSeatNumber())) {
                throw new HandManagementException.SeatOccupiedException(player.getSeatNumber());
            }

            // 检查玩家ID重复
            if (!playerIds.add(player.getPlayerId())) {
                throw new HandManagementException.DuplicateFieldException();
            }

            // Hero概念已移除，不再统计Hero数量
        }

        // Hero概念已移除，任何玩家都可以进行AI分析
    }

    /**
     * 验证单个玩家
     */
    private static void validatePlayer(CreateHandRequest.PlayerInfo player) {
        // playerId是必须的唯一标识
        if (isBlank(player.getPlayerId())) {
            throw new HandManagementException.EmptyFieldException();
        }

        if (player.getPlayerId().length() > 50) {
            throw new HandManagementException.FieldTooLongException();
        }

        if (player.getSeatNumber() == null) {
            throw new HandManagementException.EmptyFieldException();
        }

        // V4.7: 移除座位号上限限制，支持灵活的座位配置（2人到9人桌）
        // 不同平台和地区可能有不同的座位编号习惯，后端不再强制校验范围
        if (player.getSeatNumber() < 1) {
            throw new HandManagementException.InvalidSeatException();
        }

        // V4.1: player_name字段已移除，不再验证

        if (player.getStack() == null) {
            throw new HandManagementException.InvalidStackException(player.getPlayerId(), "stack required");
        }

        if (player.getStack().compareTo(BigDecimal.ZERO) <= 0) {
            throw new HandManagementException.InvalidStackException(player.getPlayerId(), "stack must be positive");
        }

        // V4.1: hole_cards在创建手牌时不再提供（改为AI分析时提供），不再验证
    }

    /**
     * 验证庄家按钮位置
     */
    private static void validateButtonSeat(CreateHandRequest request) {
        if (request.getButtonSeat() == null) {
            throw new HandManagementException.HandInvalidStateException("null button", "button_seat required");
        }

        // V4.7: 移除按钮座位号上限限制，支持灵活的座位配置（2人到9人桌）
        // 不同平台和地区可能有不同的座位编号习惯，后端不再强制校验范围
        if (request.getButtonSeat() < 1) {
            throw new HandManagementException.HandInvalidStateException("invalid button " + request.getButtonSeat(),
                "button must be >= 1");
        }

        // 验证按钮位置对应实际玩家
        boolean validButtonSeat =
            request.getPlayers().stream().anyMatch(p -> request.getButtonSeat().equals(p.getSeatNumber()));

        if (!validButtonSeat) {
            throw new HandManagementException.HandInvalidStateException("button seat not occupied",
                "button must be on an actual player seat");
        }
    }

    /**
     * 验证底牌
     */
    private static void validateHoleCards(String playerName, List<String> holeCards) {
        if (holeCards.size() != 2) {
            throw new HandManagementException.InvalidCardsException();
        }

        for (String card : holeCards) {
            if (!CARD_PATTERN.matcher(card).matches()) {
                throw new HandManagementException.InvalidCardsException();
            }
        }

        // 检查重复底牌
        if (holeCards.get(0).equals(holeCards.get(1))) {
            throw new HandManagementException.DuplicateFieldException();
        }
    }

    /**
     * 验证位置信息 - 前端驱动模式，actionOrder完全可选
     * V4.7: 移除位置值校验，支持灵活的位置命名（前端驱动架构）
     */
    private static void validatePositionInfo(CreateHandRequest request) {
        // 所有玩家都必须提供position信息
        for (CreateHandRequest.PlayerInfo player : request.getPlayers()) {
            String position = player.getPosition();
            
            if (isBlank(position)) {
                throw new HandManagementException.HandInvalidStateException(
                    "missing position for player " + player.getPlayerId() + " - position is required for all players"
                );
            }
            
            // 去除可能的空格并统一格式
            position = position.trim().toUpperCase();
            
            // V4.7: 不再限制position的具体值，支持不同平台的命名习惯
            // 只要前端提供了有效的position字符串即可（非空、非空白）
            // 例如：BTN, SB, BB, UTG, MP, HJ, CO, LJ, UTG+1 等均可
            
            // 更新玩家的position以确保格式一致
            player.setPosition(position);
        }
        
        // actionOrder是完全可选的字段
        // 前端可以根据业务需要选择是否提供，后端不强制要求
        // 这符合"前端驱动"的架构理念
        
        // 验证位置唯一性
        List<String> allPositions = request.getPlayers().stream()
                .map(CreateHandRequest.PlayerInfo::getPosition)
                .filter(pos -> pos != null)  // 额外的null安全检查
                .map(pos -> pos.trim().toUpperCase())  // 统一格式
                .collect(java.util.stream.Collectors.toList());
        
        Set<String> uniquePositions = new java.util.HashSet<>(allPositions);
        
        // 检查是否有null位置（所有玩家都应该有位置）
        long playersWithoutPosition = request.getPlayers().stream()
                .map(CreateHandRequest.PlayerInfo::getPosition)
                .filter(pos -> pos == null || pos.trim().isEmpty())
                .count();
        
        if (playersWithoutPosition > 0) {
            String playersWithoutPosDetails = request.getPlayers().stream()
                    .filter(player -> player.getPosition() == null || player.getPosition().trim().isEmpty())
                    .map(player -> String.format("Player[%s/%d]: pos='%s'", 
                            player.getPlayerId(), 
                            player.getSeatNumber(), 
                            player.getPosition()))
                    .collect(java.util.stream.Collectors.joining(", "));
            
            throw new HandManagementException.HandInvalidStateException(
                "some players missing position information: " + playersWithoutPosDetails + " - " + playersWithoutPosition + " players have null or empty position, all player details: " + getPlayerPositionDetails(request)
            );
        }
        
        // 检查位置是否有重复
        if (allPositions.size() != uniquePositions.size()) {
            // 找出具体的重复位置
            java.util.Map<String, Long> positionCounts = allPositions.stream()
                    .collect(java.util.stream.Collectors.groupingBy(
                            java.util.function.Function.identity(), 
                            java.util.stream.Collectors.counting()
                    ));
            
            String duplicates = positionCounts.entrySet().stream()
                    .filter(entry -> entry.getValue() > 1)
                    .map(entry -> entry.getKey() + "(" + entry.getValue() + ")")
                    .collect(java.util.stream.Collectors.joining(", "));
            
            throw new HandManagementException.HandInvalidStateException(
                "duplicate positions detected: " + duplicates + " - all positions: " + allPositions + ", player details: " + getPlayerPositionDetails(request)
            );
        }
    }
    
    /**
     * 获取玩家位置详细信息，用于调试
     */
    private static String getPlayerPositionDetails(CreateHandRequest request) {
        return request.getPlayers().stream()
                .map(player -> String.format("Player[%s/%d]: pos='%s'", 
                        player.getPlayerId(), 
                        player.getSeatNumber(), 
                        player.getPosition()))
                .collect(java.util.stream.Collectors.joining(", "));
    }
    
    /**
     * 验证手牌ID唯一性
     * V4.7: 支持 (table_id, hand_id) 组合唯一性
     * 同一个 hand_id 可以在不同 table_id 中存在，但在同一 table_id 内必须唯一
     */
    private static void validateHandIdUniqueness(String handId, String tableId, java.util.function.BiPredicate<String, String> handExistsChecker) {
        if (handExistsChecker.test(handId, tableId)) {
            throw new HandManagementException.HandInvalidStateException(
                "hand_id already exists: " + handId + " at table: " + tableId,
                "(table_id, hand_id) combination must be unique");
        }
    }

    /**
     * 检查字符串是否为空
     */
    private static boolean isBlank(String str) {
        return str == null || str.trim().isEmpty();
    }
}
